/*
* Copyright 2006-2007 Javector Software LLC
*
* Licensed under the GNU General Public License, Version 2 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.gnu.org/copyleft/gpl.html
*
* THE SOURCE CODE AND ACCOMPANYING FILES ARE PROVIDED WITHOUT ANY WARRANTY,
* WRITTEN OR IMPLIED.
*
* The copyright holder provides this software under other licenses for those
* wishing to include it with products or systems not licensed under the GPL.
* Contact licenses@javector.com for more information.
*/
package com.javector.soashopper.amazon;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.xml.ws.Holder;

import org.apache.log4j.Logger;


import com.amazon.webservices.awsecommerceservice._2007_02_22.AWSECommerceService;
import com.amazon.webservices.awsecommerceservice._2007_02_22.AWSECommerceServicePortType;
import com.amazon.webservices.awsecommerceservice._2007_02_22.Item;
import com.amazon.webservices.awsecommerceservice._2007_02_22.ItemLookupRequest;
import com.amazon.webservices.awsecommerceservice._2007_02_22.ItemSearchRequest;
import com.amazon.webservices.awsecommerceservice._2007_02_22.Items;
import com.amazon.webservices.awsecommerceservice._2007_02_22.OfferListing;
import com.amazon.webservices.awsecommerceservice._2007_02_22.Offers;
import com.amazon.webservices.awsecommerceservice._2007_02_22.OperationRequest;
import com.javector.soashopper.Category;
import com.javector.soashopper.Offer;
import com.javector.soashopper.Price;
import com.javector.soashopper.ShopperImp;
import com.javector.soashopper.ebay.EBayOfferImp;
import com.javector.util.TestDataGenerator;
import com.javector.util.TypeConverter;
import com.javector.util.Util;

/**
 * AmazonShopperImp is a wrapper factory class around the JAX-WS compiled classes
 * that represent the Amazon WSDL. It provides an intuitive interface for
 * accessing Amazon E-Commerce Services that shields the business application from the complexity of
 * the underlying Amazon WSDL. In addition, it provides for separation of concerns
 * in that AmazonShopperImp provides a consistent interface to business applications
 * that does not need to be changed or recompiled each time the Amazon WSDL
 * changes. Business applications should use this class for integration with
 * Amazon rather than using the WSDL compiled classes generated by JAX-WS.
 */
public class AmazonShopperImp extends ShopperImp {

  private Logger log = Logger.getLogger(AmazonShopperImp.class);
  protected AWSECommerceServicePortType port = null;
  protected String awsAccessKeyId = "";

  public AmazonShopperImp(String accessKeyId) {

    AWSECommerceService svc = new AWSECommerceService();
    port = svc.getAWSECommerceServicePort();
    this.awsAccessKeyId = accessKeyId;

  }


  /*
   * 
   *     
   * @see com.javector.soashopper.SOAShopperServiceImpl#offerSearch(java.lang.String,
   *      com.javector.soashopper.Category, com.javector.soashopper.Price,
   *      com.javector.soashopper.Price)
   */
  @Override
  public List<Offer> offerSearch(String keywords, Category category,
      Price lowprice, Price highprice) {
    List<Offer> retVal = new ArrayList<Offer>();
    List<String> itemIdList = getItemIds(keywords, category, lowprice, highprice);
    int numOfItems = itemIdList.size();
    for (int i=0; i<numOfItems; i=i+10) {
    	int top = Math.min(i+9, numOfItems-1);
    	retVal.addAll(getOffers(itemIdList.subList(i, top), lowprice, highprice));
    }
    return retVal;
  }
  
  private List<Offer> getOffers(List<String> itemIds, 
      Price lowprice, Price highprice) {
    if (itemIds.size() > 10) {
      throw new RuntimeException("Item list exceeds 10 elements.");
    }
    List<Offer> offerList = new ArrayList<Offer>();
    ItemLookupRequest itemLookupRequest = new ItemLookupRequest();
    itemLookupRequest.getItemId().addAll(itemIds);
    itemLookupRequest.setCondition("All");
    itemLookupRequest.setMerchantId("All");
    List<String> responseGroup = itemLookupRequest.getResponseGroup();
    responseGroup.add("ItemAttributes");
    responseGroup.add("OfferFull");
    responseGroup.add("Images");
    ArrayList<ItemLookupRequest> requestList = 
      new ArrayList<ItemLookupRequest>();
    requestList.add(itemLookupRequest);
    int currentOfferPage = 1;
    int totalOfferPages = 1;
    int totalOffers = 0;
    do {
      itemLookupRequest.setOfferPage(BigInteger.valueOf(currentOfferPage));
      Holder<List<Items>> itemsHolder = new Holder<List<Items>>();  
      Holder<OperationRequest> operationRequest = // response info about the request 
        new Holder<OperationRequest>();
      try {
        port.itemLookup(
            null, //marketplaceDomain 
            awsAccessKeyId, 
            null, //subscriptionId 
            null, //associateTag 
            null, //xmlEscaping 
            null, //validate, 
            null, //shared, 
            requestList, 
            operationRequest, 
            itemsHolder);
      } catch (Exception e) {
        throw new RuntimeException(
            "AWSECommerceServicePortType.itemSearch() threw an Exception", e);
      }
      List<Items> itemsList = itemsHolder.value;
      if (itemsList == null) { 
        log.debug("List<Items> is null.");
        continue; }
      Items items = itemsList.get(0);
      if (items == null) { 
        log.debug("No items returned."); 
        continue; }
      if (currentOfferPage == 1) {
        for (Item item : items.getItem()) {
          if (item != null) {
            int pages = item.getOffers().getTotalOfferPages().intValue();
            int offers = item.getOffers().getTotalOffers().intValue();
            totalOfferPages = Math.max(totalOfferPages, pages);
            totalOffers += offers;
          }
        }
        log.debug("totalOfferPages = " + totalOfferPages + 
            "  totalOffers = " + totalOffers);
      }
      for (Item item : items.getItem()) {
        if (item != null) {
          Offers offers = item.getOffers();
          if (offers != null) {
            for (com.amazon.webservices.awsecommerceservice._2007_02_22.Offer 
                amazonOffer : offers.getOffer()) {
              if (withinPriceRange(amazonOffer, lowprice, highprice)) {
                offerList.add(new Offer(new AmazonOfferImp(item, amazonOffer)));
              }
            }
          }
        }
      }
      currentOfferPage++;
    } while (currentOfferPage <= totalOfferPages);
    return offerList;
  }


  private boolean withinPriceRange(com.amazon.webservices.awsecommerceservice._2007_02_22.Offer amazonOffer, Price lowprice, Price highprice) {
    if (amazonOffer == null) { return false; }
    List<OfferListing> offerListingList = amazonOffer.getOfferListing();
    if (offerListingList == null || offerListingList.get(0) == null) {
      return false;
    }
    com.amazon.webservices.awsecommerceservice._2007_02_22.Price p = 
      offerListingList.get(0).getPrice();
    if (p == null) { return false; }
    Price offerPrice = (new TypeConverter()).toPrice(p);
    return offerPrice.ge(lowprice) && offerPrice.le(highprice);
  }


  private List<String> getItemIds(String keywords, Category category,
      Price lowprice, Price highprice) {
    List<String> itemIds = new ArrayList<String>();
    TypeConverter tc = new TypeConverter();
    ItemSearchRequest itemSearchRequest = new ItemSearchRequest();
    if (category != null) {
      AmazonStoreCategory asc = tc.toAmazonStoreCategory(category);
      itemSearchRequest.setSearchIndex(asc.getSearchIndex());
      itemSearchRequest.setBrowseNode(asc.getBrowseNode());
    }
    itemSearchRequest.setKeywords(keywords);
    if (highprice != null) {
      BigInteger maxPennies = 
        BigInteger.valueOf(Double.valueOf(highprice.getValue()*100).longValue());
      itemSearchRequest.setMaximumPrice(maxPennies);
    }
    if (lowprice != null) {
      BigInteger minPennies = 
        BigInteger.valueOf(Double.valueOf(lowprice.getValue()*100).longValue());
      itemSearchRequest.setMinimumPrice(minPennies);
    }
    itemSearchRequest.setAvailability("Available");
    itemSearchRequest.setCondition("All");
    itemSearchRequest.setMerchantId("All");
    List<String> responseGroup = itemSearchRequest.getResponseGroup();
    responseGroup.add("ItemIds"); // get the IDs, the process further
    ArrayList<ItemSearchRequest> requestList = 
      new ArrayList<ItemSearchRequest>();
    requestList.add(itemSearchRequest);
    int currentPage = 1;
    int totalPages = 1;
    do {
      itemSearchRequest.setItemPage(BigInteger.valueOf(currentPage));
      Holder<List<Items>> itemsHolder = new Holder<List<Items>>();  
      Holder<OperationRequest> operationRequest = // response info about the request 
        new Holder<OperationRequest>();
      try {
        port.itemSearch(
            null, //marketplaceDomain 
            awsAccessKeyId, 
            null, //subscriptionId 
            null, //associateTag 
            null, //xmlEscaping 
            null, //validate, 
            null, //shared, 
            requestList, 
            operationRequest, 
            itemsHolder);
      } catch (Exception e) {
        throw new RuntimeException(
            "AWSECommerceServicePortType.itemSearch() threw an Exception", e);
      }
      List<Items> itemsList = itemsHolder.value;
      if (itemsList == null) { 
        log.debug("List<Items> is null.");
        return itemIds; }
      Items items = itemsList.get(0);
      if (items == null) { 
        log.debug("No items returned."); 
        return itemIds; }
      if (currentPage == 1) {
        int itemPages = items.getTotalPages().intValue();
        int itemResults = items.getTotalResults().intValue();
        log.debug("[ItemIds] itemPages = " + itemPages + "  itemResults = " + itemResults);
        totalPages = itemPages;
      }
      for (Item item : items.getItem()) {
        if (item != null) {
          itemIds.add(item.getASIN());
        }
      }
      currentPage++;
    } while (currentPage <= totalPages);
    return itemIds;
  }


}
